home *** CD-ROM | disk | FTP | other *** search
- /******************************************************************************
- ** **
- ** Module: SR_Math.c **
- ** **
- ** **
- ** Purpose: Various mathematical utility functions **
- ** **
- ** **
- ** **
- ** Copyright (C) 1996 Apple Computer, Inc. All rights reserved. **
- ** **
- ** **
- *****************************************************************************/
- #include <assert.h>
- #include <math.h>
-
- #include "QD3D.h"
- #include "QD3DMath.h"
- #include "QD3DGeometry.h"
-
- #include "SR.h"
- #include "SR_Math.h"
-
-
- /*===========================================================================*\
- *
- * Routine: SRTriangle_GetNormal()
- *
- * Comments: Compute the face normal for a triangle. If the triangle
- * has a normal in its attribute set, use that; otherwise,
- * compute the normal from the vertices.
- *
- \*===========================================================================*/
-
- TQ3Status SRTriangle_GetNormal(
- TQ3TriangleData *triangleData,
- TQ3Vector3D *normal)
- {
- assert(triangleData != NULL);
-
- /*
- * Check for face normal for triangle. Use it if it's there, but assume
- * it's normalized, as per the rule.
- */
- if ((triangleData->triangleAttributeSet != NULL) &&
- (Q3AttributeSet_Get(
- triangleData->triangleAttributeSet,
- kQ3AttributeTypeNormal,
- normal) == kQ3Success)) {
- } else {
- /*
- * Otherwise, compute plane normal, and normalize.
- */
- Q3Point3D_CrossProductTri(
- &triangleData->vertices[0].point,
- &triangleData->vertices[1].point,
- &triangleData->vertices[2].point,
- normal);
- Q3Vector3D_Normalize(normal, normal);
- }
-
- return (kQ3Success);
- }
-
-
- /*===========================================================================*\
- *
- * Routine: SRMatrix_LUDecomposeSingular3x3()
- *
- * Comments: Decompose a singular 3x3 matrix "A" into PA = LU.
- *
- * P is the permutation matrix for the row exchanges required
- * by pivoting.
- * L is a lower triangular matrix with ones on the diagonal.
- * U is an upper echelon matrix. For nonsingular matrices,
- * it is upper triangular.
- *
- * Upon return, this routine returns LU in place of the original
- * matrix. U occupies the upper triangular elements. L occupies
- * the elements below the diagonal. P is represented in "short-
- * hand" form by the three-element vector indexPtr. If the
- * number of row exchanges is even, then *rowInterchanges is 1.0,
- * otherwise, -1.0. The rank of the matrix is returned in *rank.
- *
- * To fully understand this routine, read Chapters 1-3 of
- * Gilbert Strang's book, Linear Algebra and Its Applications.
- *
- \*===========================================================================*/
-
- /*
- * Macros for supporting SRMatrix_LUDecomposeSingular3x3
- */
- #define FLOAT_TOLERANCE 1.0e-5
- #define EQUALS_ZERO(a,e) (FABS(a) < (e))
- #define SWAP(f,g) \
- { \
- float t; \
- \
- t = (f); \
- (f) = (g); \
- (g) = t; \
- }
-
- void SRMatrix_LUDecomposeSingular3x3(
- float matrix[3][3],
- long indexPtr[3],
- float *rowInterchanges,
- long *rank)
- {
- long i, j;
- float f;
- float pivot;
- long iPivot;
- float max[3];
-
- /* Initialize parameters */
- *rowInterchanges = 1.0;
- for (i = 0; i < 3; i++) {
- indexPtr[i] = i;
- }
- *rank = 3;
-
- /* Determine magnitude of largest element in each column */
- for (j = 0; j < 3; j++) {
- max[j] = 0.0;
- for (i = 0; i < 3; i++) {
- if ((f = FABS(matrix[i][j])) > max[j]) {
- max[j] = f;
- }
- }
- }
-
- /*
- * Initial knowledge about LU decomposition:
- *
- * |1 0 0| |? ? ?|
- * L = |? 1 0| U = |? ? ?|
- * |? ? 1| |? ? ?|
- *
- * ? = yet to be determined
- * * = determined final value
- * X = nonzero pivot
- */
-
- /* Find the row with the largest pivot in the first column */
- for (i = 0, pivot = 0.0, iPivot = 0; i < 3; i++) {
- f = matrix[i][0];
- if (FABS(f) > pivot) {
- pivot = f;
- iPivot = i;
- }
- }
-
- /* Record permutation of rows */
- if (iPivot != 0) {
- /* Swap row with largest pivot with first row */
- for (j = 0; j < 3; j++) {
- SWAP(matrix[0][j], matrix[iPivot][j]);
- }
- SWAP(indexPtr[0],indexPtr[iPivot]);
- *rowInterchanges = -(*rowInterchanges);
- }
-
- if ((max[0] == 0.0) ||
- EQUALS_ZERO(matrix[0][0] / max[0], FLOAT_TOLERANCE)) {
- /*
- * First column of U is zero:
- *
- * |1 0 0| |0 ? ?|
- * L = |? 1 0| U = |0 ? ?|
- * |? ? 1| |0 ? ?|
- */
- (*rank)--;
- matrix[0][0] = 0.0;
-
- /* Find the row with the largest pivot in the second column */
- for (i = 0, pivot = 0.0, iPivot = 0; i < 3; i++) {
- f = matrix[i][1];
- if (FABS(f) > pivot) {
- pivot = f;
- iPivot = i;
- }
- }
-
- /* Record permutation of rows */
- if (iPivot != 0) {
- /* Swap row with largest pivot with first row */
- for (j = 0; j < 3; j++) {
- SWAP(matrix[0][j], matrix[iPivot][j]);
- }
- SWAP(indexPtr[0],indexPtr[iPivot]);
- *rowInterchanges = -(*rowInterchanges);
- }
-
- if ((max[1] == 0.0) ||
- EQUALS_ZERO(matrix[0][1] / max[1], FLOAT_TOLERANCE)) {
- /*
- * Second column of U is zero:
- *
- * |1 0 0| |0 0 ?|
- * L = |? 1 0| U = |0 0 ?|
- * |? ? 1| |0 0 ?|
- */
- (*rank)--;
- matrix[0][1] = 0.0;
- matrix[1][1] = 0.0;
-
- /* Find the row with the largest pivot in the third column */
- for (i = 0, pivot = 0.0, iPivot = 0; i < 3; i++) {
- f = matrix[i][2];
- if (FABS(f) > pivot) {
- pivot = f;
- iPivot = i;
- }
- }
-
- /* Record permutation of rows */
- if (iPivot != 0) {
- /* Swap row with largest pivot with first row */
- for (j = 0; j < 3; j++) {
- SWAP(matrix[0][j], matrix[iPivot][j]);
- }
- SWAP(indexPtr[0],indexPtr[iPivot]);
- *rowInterchanges = -(*rowInterchanges);
- }
-
- if ((max[2] == 0.0) ||
- EQUALS_ZERO(matrix[0][2] / max[2], FLOAT_TOLERANCE)) {
- /*
- * Third column of U is zero:
- *
- * |1 0 0| |0 0 0|
- * L = |0 1 0| U = |0 0 0|
- * |0 0 1| |0 0 0|
- */
- (*rank)--;
- matrix[0][2] = 0.0;
- matrix[1][0] = 0.0;
- matrix[1][2] = 0.0;
- matrix[2][0] = 0.0;
- matrix[2][1] = 0.0;
- matrix[2][2] = 0.0;
- } else {
- /*
- * Third column of U has a nonzero pivot:
- * Need to calculate multiplier of L to
- * eliminate third column of of U:
- *
- * |1 0 0| |0 0 X|
- * L = |* 1 0| U = |0 0 0|
- * |* 0 1| |0 0 0|
- */
- matrix[1][0] = matrix[1][2] / matrix[0][2];
- matrix[1][2] = 0.0;
- matrix[2][0] = matrix[2][2] / matrix[0][2];
- matrix[2][1] = 0.0;
- matrix[2][2] = 0.0;
- }
- } else {
- /*
- * Second column of U has a nonzero pivot:
- * Need to calculate multiplier of L to
- * eliminate second column of of U:
- *
- * |1 0 0| |0 X *|
- * L = |* 1 0| U = |0 0 ?|
- * |* ? 1| |0 0 ?|
- */
- f = matrix[1][1] / matrix[0][1];
- matrix[1][0] = f;
- matrix[1][1] = 0.0;
- matrix[1][2] = matrix[1][2] - f * matrix[0][2];
-
- f = matrix[2][1] / matrix[0][1];
- matrix[2][0] = f;
- matrix[2][1] = 0.0;
- matrix[2][2] = matrix[2][2] - f * matrix[0][2];
-
- /* Find the row with the largest pivot in the third column */
- if (FABS(matrix[1][2]) < FABS(matrix[2][2])) {
- iPivot = 2;
- /* Swap third row (possessing largest pivot) with second row */
- for (j = 0; j < 3; j++) {
- SWAP(matrix[1][j], matrix[iPivot][j]);
- }
- SWAP(indexPtr[1], indexPtr[iPivot]);
- *rowInterchanges = -(*rowInterchanges);
- } else {
- iPivot = 1;
- }
-
- if ((max[2] == 0.0) ||
- EQUALS_ZERO(matrix[1][2] / max[2], FLOAT_TOLERANCE)) {
- /*
- * Third column of U has no nonzero pivot.
- *
- * |1 0 0| |0 X *|
- * L = |* 1 0| U = |0 0 0|
- * |* 0 1| |0 0 0|
- */
- (*rank)--;
- matrix[1][2] = 0.0;
- matrix[2][1] = 0.0;
- matrix[2][2] = 0.0;
- } else {
- /*
- * Third column of U has a nonzero pivot.
- * Need to calculate multiplier of L to
- * eliminate third column of of U:
- *
- * |1 0 0| |0 X *|
- * L = |* 1 0| U = |0 0 X|
- * |* * 1| |0 0 0|
- */
- matrix[2][1] = matrix[2][2] / matrix[1][2];;
- matrix[2][2] = 0.0;
- }
- }
- } else {
- /*
- * First column of U has a nonzero pivot.
- * Need to calculate multipliers of L to
- * eliminate first column of of U:
- *
- * |1 0 0| |X * *|
- * L = |* 1 0| U = |0 ? ?|
- * |* ? 1| |0 ? ?|
- */
- f = matrix[1][0] / matrix[0][0];
- matrix[1][0] = f;
- matrix[1][1] = matrix[1][1] - f * matrix[0][1];
- matrix[1][2] = matrix[1][2] - f * matrix[0][2];
-
- f = matrix[2][0] / matrix[0][0];
- matrix[2][0] = f;
- matrix[2][1] = matrix[2][1] - f * matrix[0][1];
- matrix[2][2] = matrix[2][2] - f * matrix[0][2];
-
- /* Find the row with the largest pivot in the second column */
- if (FABS(matrix[1][1]) < FABS(matrix[2][1])) {
- iPivot = 2;
- /* Swap third row (possessing largest pivot) with second row */
- for (j = 0; j < 3; j++) {
- SWAP(matrix[1][j], matrix[iPivot][j]);
- }
- SWAP(indexPtr[1], indexPtr[iPivot]);
- *rowInterchanges = -(*rowInterchanges);
- } else {
- iPivot = 1;
- }
-
- if ((max[1] == 0.0) ||
- EQUALS_ZERO(matrix[1][1] / max[1], FLOAT_TOLERANCE)) {
- /*
- * Second column of U has no nonzero pivot.
- *
- * |1 0 0| |X * *|
- * L = |* 1 0| U = |0 0 ?|
- * |* ? 1| |0 0 ?|
- */
- (*rank)--;
- matrix[1][1] = 0.0;
-
- /* Find the row with the largest pivot in the third column */
- if (FABS(matrix[1][2]) < FABS(matrix[2][2])) {
- iPivot = 2;
- /* Swap third row (possessing largest pivot) with second row */
- for (j = 0; j < 3; j++) {
- SWAP(matrix[1][j], matrix[iPivot][j]);
- }
- SWAP(indexPtr[1], indexPtr[iPivot]);
- *rowInterchanges = -(*rowInterchanges);
- } else {
- iPivot = 1;
- }
-
- if ((max[2] == 0.0) ||
- EQUALS_ZERO(matrix[1][2] / max[2], FLOAT_TOLERANCE)) {
- /*
- * Third column of U has no nonzero pivot.
- *
- * |1 0 0| |X * *|
- * L = |* 1 0| U = |0 0 0|
- * |* 0 1| |0 0 0|
- */
- (*rank)--;
- matrix[1][2] = 0.0;
- matrix[2][1] = 0.0;
- matrix[2][2] = 0.0;
- } else {
- /*
- * Third column of U has a nonzero pivot.
- * Need to calculate multiplier of L to
- * eliminate third column of of U:
- *
- * |1 0 0| |X * *|
- * L = |* 1 0| U = |0 0 X|
- * |* * 1| |0 0 0|
- */
- matrix[2][1] = matrix[2][2] / matrix[1][2];;
- matrix[2][2] = 0.0;
- }
- } else {
- /*
- * Second column of U has a nonzero pivot.
- * Need to calculate multipliers of L to
- * eliminate second column of of U:
- *
- * |1 0 0| |X * *|
- * L = |* 1 0| U = |0 X *|
- * |* * 1| |0 0 ?|
- */
- f = matrix[2][1] / matrix[1][1];
- matrix[2][1] = f;
- matrix[2][2] = matrix[2][2] - f * matrix[1][2];
-
- if ((max[2] == 0.0) ||
- EQUALS_ZERO(matrix[2][2] / max[2], FLOAT_TOLERANCE)) {
- /*
- * Third column of U has no nonzero pivot.
- *
- * |1 0 0| |X * *|
- * L = |* 1 0| U = |0 X *|
- * |* * 1| |0 0 0|
- */
- (*rank)--;
- matrix[2][2] = 0.0;
- } else {
- /*
- * Third column of U has a nonzero pivot.
- * The matrix has full rank.
- *
- * |1 0 0| |X * *|
- * L = |* 1 0| U = |0 X *|
- * |* * 1| |0 0 X|
- */
- }
- }
- }
- }
-
-
- /*===========================================================================*\
- *
- * Routine: SRMatrix_ComputeFlatLand()
- *
- * Comments: Computes the eye vector in local coordinates and the plane
- * equation in world coordinates when the upper-left 3x3 submatrix
- * of the local-to-world matrix has a rank of 2. The eye vector
- * in local coordinates can be used for culling, but should not
- * be used for lighting because the local-to-world matrix does
- * not preserve lengths.
- *
- * Input: matrix: the local-to-world matrix
- * luDecomp: the LU decomposition of the upper-left
- * 3x3 submatrix of the local-to-world matrix
- * parallelProjection: true for parallel projections, false for
- * perspective projections
- * eyeVectorWC: the eye vector in world coordinates
- * (required only for parallel projections)
- * eyePointWC: the eye point in world coordinates
- * (required only for perspective projections)
- *
- * Output: eyeVectorLC: the eye vector in local coordinates
- * flatWorld: the plane equation in world coordinates
- * to which all geometry is transformed with
- * the normal in the same hemisphere as the
- * eye
- *
- * Returns: kQ3Success: computation completed successfully
- * kQ3Failure: computation could not be completed
- * successfully
- *
- * To fully understand this routine, read Chapters 1-3 of
- * Gilbert Strang's book, Linear Algebra and Its Applications.
- *
- \*===========================================================================*/
-
- TQ3Status SRMatrix_ComputeFlatLand(
- TQ3Matrix4x4 *matrix,
- TQ3Matrix3x3 *luDecomp,
- TQ3Boolean parallelProjection,
- TSRVector4D *eyeVectorWC,
- TQ3RationalPoint4D *eyePointWC,
- TSRVector4D *eyeVectorLC,
- TQ3PlaneEquation *flatWorld)
- {
- long pivot0 = -1;
- long pivot1 = -1;
- long j;
- TQ3Vector3D v0;
- TQ3Vector3D v1;
- TQ3Vector3D v2;
-
- /* Search for the columns of the first two nonzero pivots */
- for (j = 0; j < 3; j++) {
- if (luDecomp->value[0][j] != 0.0) {
- pivot0 = j;
- break;
- }
- }
-
- if (pivot0 != -1) {
- for (j = pivot0 + 1; j < 3; j++) {
- if (luDecomp->value[1][j] != 0.0) {
- pivot1 = j;
- break;
- }
- }
- }
-
- if (pivot0 == -1 || pivot1 == -1) {
- /* Two nonzero pivots not found */
- return kQ3Failure;
- }
-
- /*
- * Points are represented as row vectors, and they are transformed as
- * follows: p M = q
- * where M is a homogeneous matrix, and p and q are row vectors.
- * If M is the local-to-world matrix and A is the upper-left 3x3 submatrix
- * of M, then q is in the row space of A (in the absence of translation
- * and assuming that M is affine). Given a factorization of A as
- * PA = LU, a basis for the row space of A is the basis of U. Since
- * the rank of A is 2, the first two rows are a basis for U. The space
- * spanned by these two rows is the plane in WC onto which all 3D geometry
- * in LC is transformed. The cross product of the two rows is the normal
- * of the plane. Also note: the nullspace of A is orthogonal to the row
- * space: this one dimensional space is spanned by the normal of the plane
- * in WC.
- */
- Q3Vector3D_Set(
- &v0,
- luDecomp->value[0][0],
- luDecomp->value[0][1],
- luDecomp->value[0][2]);
- Q3Vector3D_Set(&v1, 0.0, luDecomp->value[1][1], luDecomp->value[1][2]);
- Q3Vector3D_Cross(&v0, &v1, &v2);
- Q3Vector3D_Normalize(&v2, &flatWorld->normal);
-
- /*
- * A point on the plane in world coordinates (WC) is given by transforming
- * any point in local coordinates (LC) to WC. If (0, 0, 0, 1) is the
- * LC point, the WC point is simply the last row of matrix M. Substitute
- * this into the plane equation to obtain the constant D = - Ax - By - Cz.
- */
- flatWorld->constant = - matrix->value[3][0] * flatWorld->normal.x
- - matrix->value[3][1] * flatWorld->normal.y
- - matrix->value[3][2] * flatWorld->normal.z;
-
- /*
- * Each point in the column space of A has a one-to-one mapping to a point
- * in the row space of A. In WC, the eye is on one side of the plane or
- * the other. For the purposes of culling, it makes sense to put the eye
- * in LC on the same side of the plane. So the eye vector in LC should be
- * orthogonal to the column space of A. Otherwise, it would be in the
- * column space, which doesn't make sense since the eye is almost never
- * in the row space in WC. A basis for the column space consists of the
- * columns in A corresponding to those in U with nonzero pivots.
- */
- Q3Vector3D_Set(
- &v0,
- matrix->value[0][pivot0],
- matrix->value[1][pivot0],
- matrix->value[2][pivot0]);
- Q3Vector3D_Set(
- &v1,
- matrix->value[0][pivot1],
- matrix->value[1][pivot1],
- matrix->value[2][pivot1]);
- Q3Vector3D_Cross(&v0, &v1, &v2);
- Q3Vector3D_Normalize(&v2, &v2);
- SRVector3D_To4D(&v2, eyeVectorLC);
-
- /*
- * We are almost done. However, the sense of the eye vector in LC and
- * the normal of the plane equation in WC still need to be established.
- * The latter needs to point in the same hemisphere as the eye.
- */
- if (parallelProjection) {
-
- /* Parallel projection */
-
- SRVector4D_To3D(eyeVectorWC, &v2);
- if (Q3Vector3D_Dot(&flatWorld->normal, &v2) < 0.0) {
- /*
- * WC eye vector and plane normal point in opposite directions.
- * Need to reverse the sense of the plane equation.
- */
- Q3Vector3D_Negate(&flatWorld->normal, &flatWorld->normal);
- flatWorld->constant = -flatWorld->constant;
- }
- } else {
- TQ3Point3D p0;
- TQ3Point3D p1;
-
- /* Perspective projection */
-
- /*
- * A WC eye vector can be obtained by subtracting the eye point
- * by a point on the plane. The LC point (0, 0, 0, 1) transformed
- * to WC is the last row of M.
- */
- Q3RationalPoint4D_To3D(eyePointWC, &p0);
- Q3Point3D_Set(
- &p1,
- matrix->value[3][0],
- matrix->value[3][1],
- matrix->value[3][2]);
- Q3Point3D_Subtract(&p0, &p1, &v2);
- if (Q3Vector3D_Dot(&flatWorld->normal, &v2) < 0.0) {
- /*
- * WC eye vector and plane normal point in opposite directions.
- * Need to reverse the sense of the plane equation.
- */
- Q3Vector3D_Negate(&flatWorld->normal, &flatWorld->normal);
- flatWorld->constant = -flatWorld->constant;
- }
- }
-
- /*
- * The hard part to comprehend is the method for establishing the sense
- * of the eye vector in LC. We had computed the eye vector by taking
- * the cross product of v0 and v1, the two basis vectors for the column
- * space. This gave a right-handed normal vector for the two basis
- * vectors. If v0 and v1 are transformed by A, the two new vectors
- * (v0 A) and (v1 A) lie in the row space, which is a plane parallel
- * to the plane to which all geometry is transformed in WC. The
- * cross product of (v0 A) and (v1 A) either has the same or opposite
- * sense as the normal to the plane, which points in the same hemisphere
- * as the eye in WC. If it is opposite, then the eye vector in LC has
- * the opposite sense to the existing orientation.
- */
- Q3Vector3D_Transform(&v0, matrix, &v0);
- Q3Vector3D_Transform(&v1, matrix, &v1);
- Q3Vector3D_Cross(&v0, &v1, &v2);
- if (Q3Vector3D_Dot(&flatWorld->normal, &v2) < 0.0) {
- /*
- * The LC eye vector transformed into WC and plane normal point in
- * opposite directions. Need to reverse the sense of the LC eye
- * vector.
- */
- SRVector4D_Negate(eyeVectorLC, eyeVectorLC);
- }
-
- return kQ3Success;
- }
-
-
- /*===========================================================================*\
- *
- * Routine: SRVector4D_Set()
- *
- * Comments: Set a 4D vector
- *
- \*===========================================================================*/
-
- TSRVector4D *SRVector4D_Set(
- TSRVector4D *vector4D,
- float x,
- float y,
- float z,
- float w)
- {
- assert(vector4D != NULL);
-
- vector4D->x = x;
- vector4D->y = y;
- vector4D->z = z;
- vector4D->w = w;
-
- return (vector4D);
- }
-
-
- /*===========================================================================*\
- *
- * Routine: SRVector3D_To4D()
- *
- * Comments: Promote a 3D vector to 4D
- *
- \*===========================================================================*/
-
- TSRVector4D *SRVector3D_To4D(
- const TQ3Vector3D *vector3D,
- TSRVector4D *result)
- {
- assert(vector3D != NULL);
- assert(result != NULL);
-
- result->x = vector3D->x;
- result->y = vector3D->y;
- result->z = vector3D->z;
- result->w = 1.0;
-
- return (result);
- }
-
-
- /*===========================================================================*\
- *
- * Routine: SRVector4D_To3D()
- *
- * Comments: Homogenize a 4D vector down to 3D
- *
- \*===========================================================================*/
-
- TQ3Vector3D *SRVector4D_To3D(
- const TSRVector4D *vector4D,
- TQ3Vector3D *result)
- {
- float oneOverW;
-
- assert(vector4D != NULL);
- assert(result != NULL);
-
- oneOverW = 1.0 / vector4D->w;
-
- result->x = vector4D->x * oneOverW;
- result->y = vector4D->y * oneOverW;
- result->z = vector4D->z * oneOverW;
-
- return (result);
- }
-
-
- /*===========================================================================*\
- *
- * Routine: SRVector4D_Negate()
- *
- * Comments: Negate a 4D vector
- *
- \*===========================================================================*/
-
- TSRVector4D *SRVector4D_Negate(
- const TSRVector4D *vector4D,
- TSRVector4D *result)
- {
- assert(vector4D != NULL);
- assert(result != NULL);
-
- result->x = -vector4D->x;
- result->y = -vector4D->y;
- result->z = -vector4D->z;
- result->w = -vector4D->w;
-
- return (result);
- }
-
-
- /*===========================================================================*\
- *
- * Routine: SRVector4D_Normalize()
- *
- * Comments: Normalize a 4D vector
- *
- \*===========================================================================*/
-
- TSRVector4D *SRVector4D_Normalize(
- const TSRVector4D *vector4D,
- TSRVector4D *result)
- {
- float length, oneOverLength;
-
- assert(vector4D != NULL);
- assert(result != NULL);
-
- length = sqrt(vector4D->x * vector4D->x +
- vector4D->y * vector4D->y +
- vector4D->z * vector4D->z +
- vector4D->w * vector4D->w);
-
- /*
- * Check for zero-length vector
- */
- if (length < kQ3RealZero) {
- *result = *vector4D;
- return (NULL);
- } else {
- oneOverLength = 1.0 / length;
-
- result->x = vector4D->x * oneOverLength;
- result->y = vector4D->y * oneOverLength;
- result->z = vector4D->z * oneOverLength;
- result->w = vector4D->w * oneOverLength;
- }
-
- return (result);
- }
-
-
- /*===========================================================================*\
- *
- * Routine: SRVector4D_Transform()
- *
- * Comments: Transform a 4D vector by a matrix.
- *
- \*===========================================================================*/
-
- TSRVector4D *SRVector4D_Transform(
- const TSRVector4D *vector4D,
- const TQ3Matrix4x4 *matrix4x4,
- TSRVector4D *result)
- {
- TSRVector4D s;
- const TSRVector4D *sPtr;
-
- assert(vector4D != NULL);
- assert(matrix4x4 != NULL);
- assert(result != NULL);
-
- /*
- * Check if caller is bashing the vector
- */
- if (vector4D == result) {
- s = *vector4D;
- sPtr = &s;
- } else {
- sPtr = vector4D;
- }
-
- result->x = sPtr->x * matrix4x4->value[0][0] +
- sPtr->y * matrix4x4->value[1][0] +
- sPtr->z * matrix4x4->value[2][0] +
- sPtr->w * matrix4x4->value[3][0];
-
- result->y = sPtr->x * matrix4x4->value[0][1] +
- sPtr->y * matrix4x4->value[1][1] +
- sPtr->z * matrix4x4->value[2][1] +
- sPtr->w * matrix4x4->value[3][1];
-
- result->z = sPtr->x * matrix4x4->value[0][2] +
- sPtr->y * matrix4x4->value[1][2] +
- sPtr->z * matrix4x4->value[2][2] +
- sPtr->w * matrix4x4->value[3][2];
-
- result->w = sPtr->x * matrix4x4->value[0][3] +
- sPtr->y * matrix4x4->value[1][3] +
- sPtr->z * matrix4x4->value[2][3] +
- sPtr->w * matrix4x4->value[3][3];
-
- return (result);
- }
-